# -*- coding: utf-8 -*-
"""
 Created by wq.wang on 2017/12/21
"""
import logging
from functools import wraps, partial

from wserver.base.field import Field, serialize_schema

_logger = logging.getLogger(__name__)

CTYPE_JSON = "application/json"

ALLOW_METHOD = {"get", "post", 'put', 'delete'}


class WApi():
    """
    定义接口
    """

    def __init__(self, func, url="", method='post', consumes=CTYPE_JSON, produces=CTYPE_JSON):
        method = method.lower()
        assert method in ALLOW_METHOD
        self._wapper_func = func
        func = self.get_real_func(func)
        func_name = func.__name__.lower()
        method = func_name if func_name in ALLOW_METHOD else method
        self._wapi_method = method
        self._func_parse(func)
        self._func_parse_doc(func.__doc__)
        self._consumes = self._get_serialize_types(consumes)
        self._produces = self._get_serialize_types(produces)
        self.url = url

    def get_real_func(self, func):
        """
        获取实际的函数
        :param func:
        :return:
        """
        real_func = getattr(func, "__wrapped__", None)
        if not real_func:
            return func
        else:
            return self.get_real_func(real_func)

    def _func_parse(self, func):
        self._func = func
        self._wapi_argcount = self._func.__code__.co_argcount - 1  # 位置参数数量 ,除掉实例本身self参数
        self._wapi_kwonlyargcount = self._func.__code__.co_kwonlyargcount  # 关键字参数
        self._wapi_co_defualts = self._func.__defaults__ or tuple()  # 默认参数
        self._wapi_flags = self._func.__code__.co_flags  # 参数的xingshi
        self._wapi_annotations = self._func.__annotations__  # 参数注释
        self._wapi_varnames = self._func.__code__.co_varnames  # 函数局部变量
        self._wapi_parameters = self._wapi_varnames[
                                1:self._wapi_argcount + self._wapi_kwonlyargcount + 1]  # wapi参数，除掉实例本身self参数
        self._wapi_defaults = dict(zip(self._wapi_parameters[len(self._wapi_parameters) - len(self._wapi_co_defualts):],
                                       self._wapi_co_defualts))  # 参数默认值

    @property
    def _wapi_return(self):
        return self._wapi_annotations['return']

    def _func_parse_doc(self, doc):
        """
        解析函数注释
        :param doc:
        :return:
        """
        doc = doc or ""
        self._wapi_doc = doc
        for info in doc.split("\n"):
            info = info.strip()
            if info.startswith(":param"):
                self._wapi_doc_summary = ""
                break
            self._wapi_doc_summary = info
            if self._wapi_doc_summary:
                break
        # 接口详情
        self._wapi_doc_description = doc.strip()
        param_return_doc = doc.split(":return:")
        if len(param_return_doc) == 2:
            self._wapi_doc_return = param_return_doc[1].strip()
        else:
            self._wapi_doc_return = ""
        param_doc = param_return_doc[0]
        param_docs = param_doc.split(":param")
        params_info = {}
        for param_doc in param_docs:
            params = param_doc.split(":")
            if len(params) < 2:
                continue
            param_name = params[0].strip()
            param_desp = "".join(params[1:]).strip()
            params_info[param_name] = param_desp
        self._wapi_doc_param = params_info

    def _get_serialize_types(self, content_type):
        if isinstance(content_type, (tuple, list)):
            content_types = content_type
        elif isinstance(content_type, str):
            content_types = [content_type]
        else:
            raise Exception("未知类型")
        for content_type in content_types:
            assert content_type in [CTYPE_JSON]
        return content_types

    def set_service(self, path, tags, operationId):
        self._wapi_path = path
        self._wapi_tags = tags
        self._wapi_operationId = operationId

    @property
    def consumers(self):
        return self._consumes

    @property
    def produces(self):
        return self._produces

    def __call__(self, instance, *args, **kwargs):
        return self._wapper_func(instance, *args, **kwargs)

    def __get__(self, instance, owner):
        return partial(self, instance)

    @property
    def _wapi_req_param(self):
        """
        必填参数参数
        :return:
        """
        must_param_count = self._wapi_argcount + self._wapi_kwonlyargcount - len(self._wapi_co_defualts)
        return self._wapi_parameters[0:must_param_count]

    @property
    def _wapi_additionalProperties(self):
        return self._wapi_flags in (79, 75)

    def get_type_schema(self, field, funparam=False):

        field_type = self._wapi_annotations.get(field) or str
        if isinstance(field_type, Field):
            return field_type.serialize()
        else:
            schema = serialize_schema(field_type)
            if funparam:
                if field in self._wapi_doc_param:
                    schema['description'] = self._wapi_doc_param[field]
                if field in self._wapi_defaults:
                    schema['default'] = self._wapi_defaults[field]
            return schema

    @property
    def wapi_response(self):
        return {"200": {"description": self._wapi_doc_return}}

    @property
    def schema(self):
        """
        根据属性生成接口
        :return:
        """

        _schema = {
            "type": "object",
            "properties": {},
            "additionalProperties": self._wapi_additionalProperties,
            'title': self._wapi_doc_summary
        }
        if self._wapi_req_param:
            _schema["required"] = list(self._wapi_req_param)
        for param in self._wapi_parameters:
            field_type = self._wapi_annotations.get(param) or str
            field_info = serialize_schema(field_type)
            if param in self._wapi_doc_param:
                field_info['description'] = self._wapi_doc_param[param]
            if param in self._wapi_defaults:
                field_info['default'] = self._wapi_defaults[param]
            _schema["properties"].update({
                param: field_info
            })
        return _schema

    @property
    def wapi_param(self):
        params = []
        for param in self._wapi_parameters:
            field_type = self._wapi_annotations.get(param) or str
            param_info = serialize_schema(field_type)
            param_info['in'] = "query"
            if "name" not in param_info:
                param_info['name'] = param
            if param in self._wapi_doc_param:
                param_info['description'] = self._wapi_doc_param[param]
            if param in self._wapi_defaults:
                param_info['default'] = self._wapi_defaults[param]
            if param in self._wapi_req_param:
                param_info["required"] = True
            params.append(param_info)
        return params

    @property
    def swagger(self):
        if self._wapi_method in ("get",):
            parameters = self.wapi_param
        else:
            parameters = [
                {
                    "in": "body",
                    "name": "body",
                    "required": bool(self._wapi_req_param),
                    "schema": self.schema
                }
            ]
        return {
            "tags": self._wapi_tags,
            "summary": self._wapi_doc_summary,
            "description": self._wapi_doc_description,
            "operationId": self._wapi_operationId,
            "consumes": self.consumers,
            "produces": self.produces,
            "parameters": parameters,
            "responses": self.wapi_response
        }

    def parse_request(self, *args, **kwargs):
        raise NotImplementedError("未实现请求处理")

    @classmethod
    def api(cls, method="post", url=None, consumes=CTYPE_JSON, produces=CTYPE_JSON):

        def wapperd(func):
            return wraps(func)(cls(func, url=url, method=method.lower(), consumes=consumes, produces=produces))

        return wapperd
